/**
 * This file define some function of flash and eeprom
 * This file use the variate "FlashWriteBuffer[]"
 * CPU will stop in the time of writing flash
 *
 */

/*
 * XC 8数据类型
 * bit  :长度1位
 * int  :16位
 * short:16位
 * short long :24位
 * long       :32位
 */
#include"../Driver_PIC/system.h"
#include<GenericTypeDefs.h>

#define FLASH_WRITE_BLOCK 0x40

bit GIEtemp;
unsigned char FlashWriteBuffer[64];
typedef union
{
    unsigned int Full;
    struct
    {
        unsigned char Low;
        unsigned char High;
    };
} UINT16_SEG;
__inline void _FlashReadByte(unsigned short long FlashAddress , unsigned char *Destination)
{
    TBLPTR = FlashAddress ;
    asm("TBLRD*+");
    *Destination = TABLAT ;
}
__inline void _FlashReadWord(unsigned short long FlashAddress , unsigned int *Destination)
{
    UINT16_SEG buff;
    TBLPTR = FlashAddress ;
    asm("TBLRD*+");
    buff.Low = TABLAT;
    asm("TBLRD*+");
    buff.High = TABLAT;
    *Destination = buff.Full;
}

__inline void _FlashReadBytes(unsigned short long FlashAddress ,unsigned char Num,unsigned char *Destination )
{
    TBLPTR = FlashAddress ;
    while(Num --)
    {
        asm("TBLRD*+");
        *Destination++  = TABLAT;
    }
}

__inline void _FlashWriteByte(unsigned short long FlashAddress,unsigned char Data )
{
    unsigned char DataPlace;
    unsigned char ReadNum;
    ReadNum = 0;

    TBLPTR = FlashAddress;
    DataPlace = TBLPTRL & 0B00111111;                                            //One block equale to 64 bytes
    TBLPTRL &= 0B11000000;                                                       //Get the block number
    
    while(ReadNum < FLASH_WRITE_BLOCK)
    {
        asm("TBLRD*+");
        FlashWriteBuffer[ReadNum ++] = TABLAT ;
    }
    FlashWriteBuffer[DataPlace] = Data;
    //Earase flash:This step is necessary ,because the write process only can become 1 to 0;
    TBLPTR = FlashAddress;
    EECON1bits.EEPGD = 1;  // point to Flash program memory
    EECON1bits.CFGS = 0;   // access Flash program memory
    EECON1bits.WREN = 1;
    EECON1bits.FREE = 1;
    GIEtemp          = INTCONbits.GIE;
    INTCONbits.GIE   = 0;
    EECON2 = 0x55;
    EECON2 = 0xAA;
    EECON1bits.WR = 1;
    INTCONbits.GIE = GIEtemp;
    //Update Flash,write process start
    TBLPTR = FlashAddress;
    TBLPTRL &= 0B11000000;
     ReadNum = 0;
     while(ReadNum < FLASH_WRITE_BLOCK)
    {
        TABLAT = FlashWriteBuffer[ReadNum++] ;
        asm("TBLWT*+");  
    }
    TBLPTR = FlashAddress;           //In the end of write TBALAT ,TBLPTR points to the next block.
    EECON1bits.FREE  = 0;
    EECON1bits.EEPGD = 1 ;
    EECON1bits.CFGS  = 0 ;
    EECON1bits.WREN  = 1 ;
    GIEtemp          = INTCONbits.GIE;
    INTCONbits.GIE   = 0;
    EECON2 = 0x55 ;
    EECON2 = 0x0AA ;
    EECON1bits.WR = 1;             /*/*启动程序存储器的写周期*/
    INTCONbits.GIE = GIEtemp;
    EECON1bits.WREN = 0;
}

__inline void _FlashWriteWord(unsigned short long FlashAddress,unsigned int Data)  //Advice : Provide even address.
{                                                                                   //If the address is 64*x+63,data will not be writen.
    UINT16_SEG DataBuff;
    unsigned char DataPlace;
    unsigned char ReadNum;

    ReadNum = 0;
    DataBuff.Full= Data;
    TBLPTR = FlashAddress;
    DataPlace = TBLPTRL & 0B00111111;
    if(DataPlace == 63)
        return;
    TBLPTRL &= 0B11000000;

    while(ReadNum < FLASH_WRITE_BLOCK)
    {
        asm("TBLRD*+");
        FlashWriteBuffer[ReadNum ++] = TABLAT ;
    }
    FlashWriteBuffer[DataPlace++] = DataBuff.Low;
    FlashWriteBuffer[DataPlace]   = DataBuff.High;
   //Earase flash:This step is necessary ,because the write process only can become 1 to 0;
    TBLPTR = FlashAddress;
    EECON1bits.EEPGD = 1;  // point to Flash program memory
    EECON1bits.CFGS = 0;   // access Flash program memory
    EECON1bits.WREN = 1;
    EECON1bits.FREE = 1;
    GIEtemp          = INTCONbits.GIE;
    INTCONbits.GIE   = 0;
    EECON2 = 0x55;
    EECON2 = 0xAA;
    EECON1bits.WR = 1;
    INTCONbits.GIE = GIEtemp;
    //Update Flash,write process start
    TBLPTR = FlashAddress;
    TBLPTRL &= 0B11000000;
     ReadNum = 0;
     while(ReadNum < FLASH_WRITE_BLOCK)
    {
        TABLAT = FlashWriteBuffer[ReadNum++] ;
        asm("TBLWT*+");
    }
    TBLPTR = FlashAddress;           //In the end of write TBALAT ,TBLPTR points to the next block.
    EECON1bits.FREE  = 0;
    EECON1bits.EEPGD = 1 ;
    EECON1bits.CFGS  = 0 ;
    EECON1bits.WREN  = 1 ;
    GIEtemp          = INTCONbits.GIE;
    INTCONbits.GIE   = 0;
    EECON2 = 0x55 ;
    EECON2 = 0x0AA ;
    EECON1bits.WR = 1;             /*/*启动程序存储器的写周期*/
    INTCONbits.GIE = GIEtemp;
    EECON1bits.WREN = 0;
}
__inline void _FlashWriteBytes(unsigned short long FlashAddress,unsigned char amount,unsigned char *buff)
{                                                                 //All the Data must be in the common block.
    unsigned char DataPlace;                                      //else the Data will not be writen. no signal return.
    unsigned char ReadNum;
    if(amount == 0)
        return;
    TBLPTR = FlashAddress;
    DataPlace = TBLPTRL & 0B00111111;
    if((DataPlace + amount) > 64 )                                //the block is 64 bytes
        return;
    TBLPTRL &= 0B11000000;
    ReadNum = 0;
    while(ReadNum < FLASH_WRITE_BLOCK)
    {
        asm("TBLRD*+");
        FlashWriteBuffer[ReadNum ++] = TABLAT ;
    }
    while(amount --)
    {
        FlashWriteBuffer[DataPlace++] = *buff++;
    }
      //Earase flash:This step is necessary ,because the write process only can become 1 to 0;
    TBLPTR = FlashAddress;
    EECON1bits.EEPGD = 1;  // point to Flash program memory
    EECON1bits.CFGS = 0;   // access Flash program memory
    EECON1bits.WREN = 1;
    EECON1bits.FREE = 1;
    GIEtemp          = INTCONbits.GIE;
    INTCONbits.GIE   = 0;
    EECON2 = 0x55;
    EECON2 = 0xAA;
    EECON1bits.WR = 1;
    INTCONbits.GIE = GIEtemp;
    //Update Flash,write process start
    TBLPTR = FlashAddress;
    TBLPTRL &= 0B11000000;
     ReadNum = 0;
     while(ReadNum < FLASH_WRITE_BLOCK)
    {
        TABLAT = FlashWriteBuffer[ReadNum++] ;
        asm("TBLWT*+");
    }
    TBLPTR = FlashAddress;           //In the end of write TBALAT ,TBLPTR points to the next block.
    EECON1bits.FREE  = 0;
    EECON1bits.EEPGD = 1 ;
    EECON1bits.CFGS  = 0 ;
    EECON1bits.WREN  = 1 ;
    GIEtemp          = INTCONbits.GIE;
    INTCONbits.GIE   = 0;
    EECON2 = 0x55 ;
    EECON2 = 0x0AA ;
    EECON1bits.WR = 1;             /*启动程序存储器的写周期*/
    INTCONbits.GIE = GIEtemp;
    EECON1bits.WREN = 0;
}
/***********取自flash.h中声明的函数原型******为便于区分加_***********/
__inline void _EraseFlash(unsigned long startaddr, unsigned long endaddr)
{
    unsigned char flag=0;
    DWORD_VAL flash_addr;
    flash_addr.Val = startaddr;
		while(flash_addr.Val<endaddr)
		{
			TBLPTRU = flash_addr.byte.UB;						//Load the address to Address pointer registers
			TBLPTRH = flash_addr.byte.HB;
			TBLPTRL	= flash_addr.byte.LB;
			//*********Flash Erase sequence*****************
			EECON1bits.EEPGD = 1;  // point to Flash program memory
			EECON1bits.CFGS = 0;  // access Flash program memory
			EECON1bits.WREN = 1;
			EECON1bits.FREE = 1;
			if(INTCONbits.GIE)
			{
				INTCONbits.GIE = 0;
				flag=1;
			}
			EECON2 = 0x55;
			EECON2 = 0xAA;
			EECON1bits.WR = 1;
			if(flag)
				INTCONbits.GIE = 1;
			flash_addr.Val = flash_addr.Val + FLASH_WRITE_BLOCK;                      //The Block is 64 bytes _This line is replaced
		}
}

/*以下程序取自库文件read_B.c中的Read_b_eep函数*/
/*****************256字节**********************/
unsigned char ReadChar_eep( unsigned char badd )
{
	EEADR = (badd & 0x0ff);
  	EECON1bits.CFGS = 0;
	EECON1bits.EEPGD = 0;
	EECON1bits.RD = 1;
	asm("nop");							//Nop may be required for latency at high frequencies
	asm("nop");							//Nop may be required for latency at high frequencies
	return ( EEDATA );                                              // return with read byte
}
/*以下程序取自库文件write_B.c中的Write_b_eep函数*/
/******************256字节*********************/
void WriteChar_eep( unsigned char badd,unsigned char bdat )
{
	EEADR = (badd & 0x0ff);
  	EEDATA = bdat;
  	EECON1bits.EEPGD = 0;
	EECON1bits.CFGS = 0;
	EECON1bits.WREN = 1;
	GIEtemp = INTCONbits.GIE;
	INTCONbits.GIE = 0;
	EECON2 = 0x55;
	EECON2 = 0xAA;
	EECON1bits.WR = 1;
	while(EECON1bits.WR);			                 	//Wait till the write completion
	INTCONbits.GIE = GIEtemp;
	EECON1bits.WREN = 0;
}
/*********************EEP阵列刷新***************/
/************根据使用手册的例7.3编写************/
void EEPRefurbish()
{
    EEADR            = 0;
    EECON1bits.CFGS  = 0;
    EECON1bits.EEPGD = 0;
    GIEtemp          = INTCONbits.GIE;
    INTCONbits.GIE   = 0;
    EECON1bits.WREN  = 1;

    do
    {
        EECON1bits.RD = 1;
        EECON2        = 0x55;
	EECON2        = 0xAA;
        EECON1bits.WR = 1;
        while(EECON1bits.WR);
    } while((++EEADR) == 0);
    EECON1bits.WREN   = 0;
    INTCONbits.GIE    = GIEtemp;
}